js 基础面试题总结
2021-04-14 03:43:00 # fontend

值传递和值引用

js 数据类型分为 基本类型和引用类型

基本类型: Number Boolean String Undefined Null Symbol
引用类型: Object Function Array Date RegExp Math 其他类 object 的实例对象

基本类型的存储方式: 变量和值都在栈内存中
引用类型的存储方式: 变量名存储在栈内存中,值存储在堆内存中,堆内存中会提供一个引用地址指向堆内存中的值,而这个引用地址是储存在栈内存中的。

  1. 打印以下结果

    1
    2
    3
    4
    5
    6
    7
    8
    9
    function changeObjProperty(o) {
    o.siteUrl = "http://www.baidu.com"
    o = new Object()
    o.siteUrl = "http://www.google.com"
    }
    let webSite = new Object();
    changeObjProperty(webSite);
    console.log(webSite.siteUrl);
    <!-- 原因:changeObjProperty 的参数是变量的引用, new Object 是创建了一个新对象,对外面的 webSite 不产生影响 -->
  2. 检查对象类型

    1
    2
    3
    1. typeof 
    2. instanceof
    3. Object.prototype.toString.call(args) // 可检查所有对象类型
  1. 深拷贝 & 浅拷贝

    这里的拷贝要区别于赋值操作, 对基本类型数据的赋值操作是值传递,对引用类型的数据的赋值操作是引用传递,而浅拷贝是新创建了对象,对第一层数据进行复制,子对象的话则是引用传递
    

    浅拷贝:创建新对象,对第一层属性进行赋值,子对象的话依然是引用传递
    深拷贝:创建新对象,对对象和子对象进行完全的复制,被拷贝对象发生任何变化不会影响到新对象

    举例说明赋值和浅拷贝的区别:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    var obj = {
    name: 'near',
    age: 18,
    language: {
    value: 'zh'
    }
    }

    var obj2 = obj
    var obj3 = copy(obj)

    // 浅拷贝
    function copy(obj) {
    let newObj = {}
    for(let key in obj) {
    if (obj.hasOwnProperty(key)) {
    newObj[key] = obj[key]
    }
    }
    return newObj
    }

    obj.name = 'Lisa'
    console.log(obj2.name) // 'Lisa' 值引用,指向同一块内存地址,所以会同步改变
    console.log(obj3.name) // 'near' 第一层基本数据类型是值传递

    obj.language.value = 'en'
    console.log(obj2.language.value) // 'en' 子对象是值引用,所以会互相影响
    console.log(obj3.language.value) // 'en'
常见的浅拷贝
  1. 直接赋值

    1
    2
    3
    4
    let obj = { a: 2 }
    let obj2 = obj
    obj2.a = 1
    obj.a // 1
  2. 对象解构

  3. for in 循环赋值
  4. ES6 的 Object.assign

    1
    2
    3
    4
    5
    6
    let obj = { a: 20, b: { c: 3 } }
    let obj2 = Object.assign({}, obj)
    obj2.a = 10
    obj.a // 20
    obj2.b.c = 10
    obj.b.c // 10
常见的深拷贝
  1. JSON.parse(JSON.stringify())

    对象属性比较简单的时候可以使用,此方法会出现的问题有:

    1. 值为 undefined 的属性转换后会丢失
    2. 值为 Symbol 类型的属性在转换后丢失
    3. 值为 RegExp 对象的属性在转换后变成了空对象
    4. 值为 函数对象的属性在转换后丢失
    5. 值为 Date 对象的属性在转换后变成了字符串
    6. 会抛弃对象的 constructor,所有的构造函数会指向 Object
    7. 对象的循环引用会抛出错误。
  2. 手动实现 deepClone

    解决问题:

    1. 循环引用过多导致爆栈
    2. 特殊值拷贝属性丢失
    3. 会抛弃对象的 constructor,所有的构造函数会指向 Object
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
function deepClone(obj, hash = new WeakMap()) {

<!-- 特殊值过滤掉 typeof null === 'object' 原生构造函数 -->

if (obj === null) return obj
if (obj instanceof RegExp) return new RegExp(obj)
if (obj instanceof Date) return new Date(obj)
if (typeof obj !== 'object') return obj

<!-- 避免循环引用爆栈 -->

if (hash.has(obj)) return hash.get(obj)

<!-- constructor 属性理论上来说不太可靠,此属性可以被修改,但是在复制场景下 不太会被修改 -->

let newObj = new obj.constructor

hash.set(obj, newObj)

<!-- symbol 的 key 是不可枚举的类型,所以 hasOwnProperty 无法遍历出来,需要单独通过 getOwnPropertySymbols 解决 -->

let symKeys = Object.getOwnPropertySymbols(obj)
if (symKeys.length) {
symKeys.forEach(item => {
newObj[item] = deepClone(obj[item], hash)
})
}

<!-- 只拷贝对象的值,不拷贝原型链上的值 -->

for (const key in obj) {
if (obj.hasOwnProperty(key)) {
newObj[key] = deepClone(obj[key], hash)
}
}

return newObj
}

对 this 对象的理解

关于 this 的一些关键性理解:

  1. this 总是指向函数的直接调用者
  2. 如果有 new 关键字,this 指向 new 出来的实例对象
  3. 在事件中,this 指向触发这个事件的对象
  4. IE 下 attachEvent 中的 this 总是指向全局对象 Window
  5. 箭头函数中,函数体内的this对象,就是定义时所在作用域的对象,而不是使用时所在的作用域的对象。
  6. 箭头函数里的 this 如果放在全局中就是 window
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
function foo() {
console.log(this.a)
}
var a = 1
foo() //1

const obj = {
a: 2,
foo: foo
}
obj.foo() //2

const c = new foo() // undefined

const obj2 = {
a: 2,
foo: () => {
console.log(this.a)
}
}

<!-- 箭头函数本身没有 this,只取决于包裹箭头函数的第一个普通函数的 this 。 此时是存在于obj,也就是window中,所以 this = window -->
obj2.foo() // 1

// this 指向被调用的函数的作用域
var name = "caibaojian.com";
var person = {
name: "kang",
pro: {
name: "Michael",
getName: function() {
return this.name;
}
}
};
console.log(person.pro.getName()); //Michael
var people = person.pro.getName; // 此时调用函数的this 指向 window
console.log(people()); //caibaojian.com


'use strict';
var name = "caibaojian.com";
var person = {
name: "kang",
pro: {
name: "Michael",
getName: function() {
console.log(this);
return this.name;
}
}
};
console.log(person.pro.getName()); //Michael
var people = person.pro.getName;
console.log(people()); //undefined 在严格模式下,如果this未被执行的上下文环境定义,那么它将会默认为undefined。

// 普通函数
function foo() {
setTimeout(function() {
console.log('id:', this.id);
});
}

var id = 21;
foo.call({ id: 42 }); //21

// 箭头函数
function foo() {
setTimeout(function() {
console.log('id:', this.id);
});
}

var id = 21;
foo.call({ id: 42 }); // 42,箭头函数中的 this 和执行的函数内部的this保持一致,此时通过 call 方法指向了 {id: 42} 所以此时输出 42


// 箭头函数
function foo() {
setTimeout(function() {
console.log('id:', this.id);
});
}

var id = 21;
foo.call(); // 21
  1. 手动实现 bind 函数。

    bind 做了什么:  
    1. 将 this 绑定到当前函数参数中
    2. 不立即执行,返回一个函数
    3. 函数参数原型上的方法还能执行
    4. this 不能在绑定到其他变量上面
    5. 待执行函数还可以传递其他参数
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    Function.prototype.bind = function(obj) {
    let args = [].slice.call(arguments, 1)
    var fn = this
    var bound = function() {
    var argguments = Array.from(argguments)
    fn.apply(this.constructor === fn ? this : obj, args.concat(argguments))
    }
    bound.prototype = fn.prototype
    return bound
    }


  1. 手动实现 call / apply 函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
<!-- 简单实现 call -->

Function.prototype.call_ = function (obj) {
obj.fn = this; // 此时this就是函数fn
obj.fn(); // 执行fn
delete obj.fn; //删除fn
};

<!-- ES5 call -->
Object.prototype.call = function(obj) {
//判断是否为null或者undefined,同时考虑传递参数不是对象情况
obj = obj ? Object(obj) : window;
var args = [];
// 注意i从1开始
for (var i = 1, len = arguments.length; i < len; i++) {
args.push("arguments[" + i + "]");
};
obj.fn = this; // 此时this就是函数fn
eval("obj.fn(" + args + ")"); // 执行fn
delete obj.fn; //删除fn
}

// ES6 call
Function.prototype.call_ = function (obj) {
obj = obj ? Object(obj) : window;
obj.fn = this;
// 利用拓展运算符直接将arguments转为数组
let args = [...arguments].slice(1);
let result = obj.fn(...args);

delete obj.fn
return result;
};


// ES5 apply
Function.prototype.apply_ = function (obj, arr) {
obj = obj ? Object(obj) : window;
obj.fn = this;
var result;
if (!arr) {
result = obj.fn();
} else {
var args = [];
// 注意这里的i从0开始
for (var i = 0, len = arr.length; i < len; i++) {
args.push("arr[" + i + "]");
};
result = eval("obj.fn(" + args + ")"); // 执行fn
};
delete obj.fn; //删除fn
return result;
};

// ES6 apply
Function.prototype.apply_ = function (obj, arr) {
obj = obj ? Object(obj) : window;
obj.fn = this;
let result;
if (!arr) {
result = obj.fn();
} else {
result = obj.fn(...arr);
};

delete obj.fn
return result;
};

隐式转换

隐式转换规则:

  1. undefined == null,结果是true。且它俩与所有其他值比较的结果都是false。
  2. String == Boolean,需要两个操作数同时转为Number。
  3. String/Boolean == Number,需要String/Boolean转为Number。
  4. Object == Primitive,需要Object转为Primitive(具体通过valueOf和toString方法)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var a = [1, 2, 3];
var b = [1, 2, 3];
var c = "1,2,3";

a == c; // true
b == c; // true 数组和字符串比较时,数组会通过逗号拼接转换为字符串
a == b; // false 两个相同数组不相等

思考题:
++[[]][+[]]+[+[]]==10 // true

// (++[[]][+[]]) + ([+[]])
// +[] = 0
// (++[[]][0]) + [0]
// var a = [[]][0]; ++a;
// var a = []; a = a+1; ++[] = 1
// 1 + [0] // [0] == '0'
// '10'

valueOf 和 toString()

事件循环, 宏任务,微任务

  1. 打印一下结果 事件循环和异步

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    async function async1() {
    console.log('async1 start');
    await async2();
    console.log('async1 end');
    }
    async function async2() {
    console.log('async2');
    }
    console.log('script start');
    setTimeout(function() {
    console.log('setTimeout');
    }, 0)
    async1();
    new Promise(function(resolve) {
    console.log('promise1');
    resolve();
    }).then(function() {
    console.log('promise2');
    });
    console.log('script end');

    // 打印结果
    script start
    async1 start
    async2
    promise1
    script end
    async1 end
    promise2
    setTimeout
  1. 事件循环和异步

    微任务:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    // setTimeout 和 promise
    const tasks = []
    const output = (i) => new Promise((reslove) => {
    setTimeout(() => {
    console.log(i);
    reslove();
    }, 1000 * i)
    });

    for(var i = 0; i<5; i++) {
    tasks.push(output(i))
    }

    Promise.all(tasks).then(() => {
    setTimeout(() => {
    console.log(i)
    }, 1000)
    });

    // 0 1 2 3 4 5

    // setTimout 和 sleep
    setTimeout(() => { console.log(1) }, 1000)

    setTimeout(() => { console.log(2) }, 0)

    setTimeout(() => {
    console.log(3)
    setTimeout(() => { console.log(4) }, 0)
    }, 800)
    function sleep(delay) {
    var start = (new Date()).getTime();
    while((new Date()).getTime() - start < delay) {
    continue;
    }
    }

    sleep(1200)
    console.log(5)
    for(var i=10;i<=15;i++) {
    setTimeout(() => {console.log(i)}, 0)
    }

    // 5 2 3 1 16*6 4


    // setTimeout process.nextTick promise
    console.log('1');
    async function async1() {
    console.log('2');
    await async2();
    console.log('3');
    }
    async function async2() {
    console.log('4');
    }

    process.nextTick(function() {
    console.log('5');
    })

    setTimeout(function() {
    console.log('6');
    process.nextTick(function() {
    console.log('7');
    })
    new Promise(function(resolve) {
    console.log('8');
    resolve();
    }).then(function() {
    console.log('9')
    })
    })

    async1();

    new Promise(function(resolve) {
    console.log('10');
    resolve();
    }).then(function() {
    console.log('11');
    });
    console.log('12');

    // 1 2 4 10 12 5 (3 11) 6 8 7 9

    // 宏任务 和 微任务的创建时机

    console.log('start');
    setTimeout(() => {
    console.log('children2');
    Promise.resolve().then(() => {
    console.log('children3');
    })
    }, 0);

    new Promise(function(resolve, reject) {
    console.log('children4');
    setTimeout(function() {
    console.log('children5');
    resolve('children6')
    }, 0)
    }).then((res) => {
    console.log('children7');
    setTimeout(() => {
    console.log(res);
    }, 0)
    })
    // 执行结果

    start
    children4
    children2
    children3
    children5
    children7
    children6

    // 说明:在没有 reslove 的时候,不会将 then 创建为微任务

    //

    const p = function() {
    return new Promise((resolve, reject) => {
    const p1 = new Promise((resolve, reject) => {
    setTimeout(() => {
    resolve(1)
    }, 0)
    resolve(2)
    })
    p1.then((res) => {
    console.log(res);
    })
    console.log(3);
    resolve(4);
    })
    }


    p().then((res) => {
    console.log(res);
    })
    console.log('end');

    // 从上向下执行,p1.then 先进入微任务队列,p.then 后进入,所以结果为:
    // 3 ‘end’ 2 4

提升(hoisting)

  1. var 声明会提升
  2. 函数声明会首先提升,但是函数表达式不会提升声明
  3. 声明统一提前,赋值原地不变
  4. 提升变量阶段: var 是提升声明,还没有被定义; 函数关键字的就已经声明和定义
  5. 条件判断语句,没有大括号时,if 条件不满足,则最近的一条语句不执行,if 满足,则后面的条件都会走; 有大括号时,不满足时外部条件都执行

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    a = 2
    var a
    console.log(a) // 2

    console.log(a) // undefined
    var a = 2


    function A() { console.log(2) }
    var A = function() { console.log(1) }
    A()
    // 1

    var A = function() { console.log(1) }
    function A() { console.log(2) }
    A()
    // 1 此时 A 已经在window 属性下,这属于重名,根据js自上而下执行,此时再次声明会被忽略

    A()
    var A = function() { console.log(1) }
    function A() { console.log(2) }
    // 2 函数声明会首先被提升,所以输出 2

    A()
    function A() { console.log(2) }
    var A = function() { console.log(1) }
    function A() { console.log(3) }
    // 3 重复的函数声明还是可以覆盖前面的,函数表达式不会声明提前

    if(true) { function B() { console.log(1) } }
    { var B = function() { console.log(2) } }
    B()
    // 2 函数声明之后,函数表达式可以改变函数;



    console.log(a, b) // undefined undefined 执行时都未定义 所以输出 undefined
    var a =12, b ='林一一' // window.b = '林一一'
    function foo(){

    console.log(a, b) // 此时 b 不带 var 将向上级作用域查找, a 因为函数内部 var 变量提升 为 undefined

    var a = b =13
    console.log(a, b) // 13 13 window.b = 13, a 此时为函数内部变量
    }
    foo()
    console.log(a, b) // 12 13 此时读取全局变量 a = 12, window.b 有foo 改变,所以为 13

    // undefined undefined
    // undefined 林--
    // 13 13
    // 12 13


    a = 0
    function foo(){
    var a =12;
    b = '林一一'
    console.log('b' in window) // b 没有声明 因此向上级作用域查找 true
    console.log(a, b) // 12 '林一一'
    }

    foo()
    console.log(b) // window.b = '林一一'
    console.log(a) // window.a = 0


    function foo(){
    console.log(a)
    a =12;
    b = '林一一'
    console.log('b' in window)
    console.log(a, b)
    }
    foo()
    // 抛出 ReferenceError: a is not defined


    fn();
    console.log(v1);
    console.log(v2);
    console.log(v3);
    function fn(){
    var v1 = v2 = v3 = 2019;
    console.log(v1);
    console.log(v2);
    console.log(v3);
    }

    // if 判断语句下的变量提升
    if(!("value" in window)){
    var value = 2019;
    }
    console.log(value); // undefined if 条件语句中无论条件是否成立里面的var或者function 声明被提升
    console.log('value' in window); // true if 条件语句中无论条件是否成立里面的变量都会提升

    var a=2;
    function a() {
    console.log(3);
    }
    console.log(typeof a);

    // number 通过 var 声明之后,根据自上而下执行机制,函数声明被忽略


    var a = 10;
    (function () {
    console.log(a)
    a = 5
    console.log(window.a)
    var a = 20;
    console.log(a)
    })()
    // undefined 10 20
    // 存在两个作用域 全局作用域和匿名函数作用域, 函数作用域内因为有 var ,存在变量名提升,所以先打印 undefined, window.a 输出的是全局变量a,为10

作用域

  1. 考察作用域

    哪些语法会创建块级作用域:

    1. try/catch,catch 分句会创建块及作用域,error 只能在内部调用
    2. with 
    3. let 会将变量绑定在任意作用域中,比如常见的 {} ,let 声明不会提升
    4.  {} 会创建块及作用域, 里面声明的变量或者函数无论条件是否满足,都会被变量提升
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    for(var i =0; i< 5;i++) { 
    setTimeout((i) => {console.log(i)}, 0)
    }
    // 5 5 5 5 5

    for(let i =0; i< 5;i++) {
    setTimeout((i) => {console.log(i)}, 0)
    }
    // 0 1 2 3 4

    for(var i =0; i< 5;i++) {
    (function(i){
    setTimeout((i) => {console.log(i)}, 0)
    })(i)
    }

    let i; // 此时的 i 仍声明在全局变量中
    for(i=0;i<5;i++) {
    setTimeout(() => {console.log(i) }, 0)
    }
    // 5 5 5 5 5

    for(let i=0;i<5;i++) { // let 将 i 绑定到for 循环中,并且是将其重新绑定到每次循环中,所以可以得到 0 1 2 3 4
    setTimeout(() => {console.log(i) }, 0)
    }


    function demo(){
    let x = b = 0;
    return x
    }
    demo()
    console.log(typeof x) x // undefined 只在函数内部声明,所以外部无法获取
    console.log(typeof b) b // number 并没有被声明,所以向上级作用域查找并最终添加到window.b ,var let const 在此场景下功效相同

正则表达式

  1. 正则表达式

    1
    2
    3
    var str = '<p> Hello </p> World <br>!</br>';
    var regx = /<[^<>]+>/g
    console.log(str.replace(regx, ''));

object 的 key值的类型转换

obj 的 key 如果是变量的话,会转换成 string

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var a = {}
var b = {key:'b'}
var c = {key:'c'}
var d = [3,5,6];
var e = true
var f = function test() { console.log(1) }
var g = Symbol(1)

a[b] = 123;
a[c] = 345;
a[d] = 333;
a[e] = 111
a[f] = 222
a[g] = 444

console.log(a[b]); // a[object Object]: 345
console.log(a[c]); // a[object Object]: 345
console.log(a[d]); // a[1,2,3]: 333
console.log(a[e]); // a['true']: 111
console.log(a[f]); // a['function test() { console.log(1) }']: 222
console.log(a[g]); // a['Symbol(1)']: 222

js 方法

  1. 考察 array 数组方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    [1,2,3,4].reduce((x,y) => {console.log(x,y); return x;})

    // 输出结果

    A: 1 2; 3 3; 6 4;
    B: 1 2; 2 3; 3 4;
    C: 1 undefined; 2 undefined; 3 undefined; 4 undefined;
    D: 1 2; undefined 3; undefined 4;

    // reduce 不改变原数组,返回新数组
  1. 考察作用域,函数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    const value = { number: 10 }
    const mutlipt = (x = { ...value }) => {
    console.log(x.number *= 2)
    }

    mutlipt()
    mutlipt()
    mutlipt(value)
    mutlipt(value)
  1. 考察加减

    1
    2
    3
    4
    5
    6
    7
    8
    9
    let num = 10
    let increaseNum = () => num++;
    let increasePassedNum = (number) => number++;

    let num1 = increaseNum();
    let num2 = increasePassedNum(num1++);

    console.log(num1);
    console.log(num2);
  1. JSON.stringify 第二个参数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    var a = {
    name: 'test',
    age: 18,
    email: 'qq.com'
    }
    var b = JSON.stringify(a, ['age', 'email'])

    // 解读:
    JSON.stringify(value, replacer)
    replacer:
    如果是函数 则以该函数对所有value做格式化处理
    如果是数组,则只有包含在这个数组中的属性名才会被序列化到最终的 JSON 字符串中;
    如果该参数为 null 或者未提供,则对象所有的属 性都会被序列化
  1. 考察 Object.defineProperty, Object.keys

    1
    2
    3
    4
    5
    6
    7
    var person = { name: 'haha' }
    Object.defineProperty(person, 'age', { value: 12 })
    console.log(person)
    console.log(Object.keys(person))

    // Object.keys 返回对象可枚举 enumerable 的属性
    // Object.defineProperty 定义的属性默认 enumerable configurable writable 为 false,get set value 为 undefined
  1. 考察 delete

    1
    2
    3
    4
    5
    6
    7
    8
    const name = '111'
    age = 18
    console.log(delete name)
    console.log(delete age)

    // 任何存在或者不存在的属性删除时 返回 true
    // 除非删除不可设置的属性时,返回 false ,使用 let / const 声明的变量
    // delete 只会删除自身属性 不会删除原型链上的属性
  1. charAt

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    var str = 'aaa we dasd'
    console.log(str.charAt(3)) // " "
    console.log(str.chatAt(0)) // a

    小驼峰字符串:

    let capitalize = (value) => {
    value = toString(value)
    let firstCode = value.charAt(0)
    let trailing = value.slice(1)
    return firstCode.toUpperCase() + trailing;
    }
  1. generator 生成器

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    function* generator(i){
    yield i
    yield i * 2
    }

    let gen = generator(10);

    console.log(gen.next().value);
    // 10
    console.log(gen.next().value);
    // 20
    console.log(gen.next().value);
    // undefined
  1. set

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // 一维数组去重 
    Array.from(new Set([1,1,2,2,3]))

    // 根据先进先出,获取最先插入的元素
    set.entries().next().value[0]

    // delete 操作返回值
    返回操作前 has 的值,存在当前值则返回 true, 不存在则返 回 false

    // 值 大小写敏感并且不能重复
  1. 运算符

    1
    2
    3
    4
    5
    3.1 3. .3 都是合法的数字
    // 以下哪些情况会输出 ’3‘
    3.toString() // 解析为 (3.)toString()
    3..toString() // (3.).toString()
    3...toString() // (3..).toString()
  2. error 的类型

    1
    2
    3
    4
    5
    RangeError:  数值变量或参数超出其有效范围
    ReferenceError: 无效的引用,比如变量未定义
    SyntaxError: eval()在解析代码的过程中发生的语法错误。
    TypeError: 变量或参数不属于有效类型
    URIError: 给 encodeURI()或 decodeURl()传递的参数无效。
  3. 手动书写 Promise.all

1
2
3
4
5
6
7
8
9
10
11
function promiseAll (promises) {
let result = promised.map(promise => {
return promise.then(res => {
return Promise.reslove(res);
}).catch(error => {
return Promise.reject(error);
})
});

return result;
}